package servlet;

import it.polimi.swimv2.entities.Feedback;
import it.polimi.swimv2.entities.User;
import it.polimi.swimv2.exception.ModifyException;
import it.polimi.swimv2.exception.UserDataException;
import it.polimi.swimv2.exception.UserNotFoundException;
import it.polimi.swimv2.remote.AbilityManagerRemote;
import it.polimi.swimv2.remote.UserManagerRemote;
import it.polimi.swimv2.remote.UserProfileManagerRemote;
import it.polimi.swimv2.util.RemoteBindingName;

import java.io.IOException;
import java.util.Collection;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Servlet implementation class UserServlet
 */
public class UserServlet extends HttpServlet {

	
	

	private static final String SESSION_USERNAME = "user";
	// Page references:
	private static final String PERSONAL_PROFILE = "personalProfile.jsp";
	private static final String MODIFY_PROFILE = "modifyProfile.jsp";
	private static final String MANAGE_ABILITIES = "manageAbilities.jsp";
	private static final String INDEX_JSP = "index.jsp";
	private static final String ERROR_JSP = "error.jsp";
	// Actions performed by this servlet:
	private static final String SHOW_PERSONAL_PROFILE = "showPersonalProfile";
	private static final String SHOW_MODIFY_PROFILE = "showModifyProfile";
	private static final String SHOW_MANAGE_ABILITIES = "showManageAbilities";
	private static final String ACT_DELETE_ABILITY = "deleteAbility";
	private static final String ACT_ADD_ABILITY = "addAbility";
	private static final String ACT_MODIFY_PERSONAL_DATA = "modifyPersonalData";
	private static final String ACT_MODIFY_PASSWORD = "modifyPassword";
	// Parameters in Http Request:
	private static final String USER_REQPARAM = SESSION_USERNAME;
	private static final String ABILITY_PARAM = "abilityName";
	private static final String PHONE_REQPARAM = "phone";
	private static final String CITY_REQPARAM = "city";
	private static final String EMAIL_REQPARAM = "email";
	private static final String FEEDBACKS_REQPARAM = "feedbacks";
	private static final String DECLARABLE_ABILITIES_REQPARAM = "declarableAbilities";
	private static final String DECLARED_ABILITIES_REQPARAM = "declaredAbilities";
	private static final String CURRENT_PWD_REQPARAM = "currentPwd";
	private static final String NEW_PWD1_REQPARAM = "newPwd1";
	private static final String NEW_PWD2_REQPARAM = "newPwd2";
	// message error values:
	private static final String ERROR_ATTRIBUTE = "error";
	private static final String SUCCESS_PWD = "successPwd";
	private static final String SUCCESS_DATA = "successData";
	private static final String MODIFY_PASSWORD_ERRORVALUE = ACT_MODIFY_PASSWORD;
	private static final String NEW_PWD_ERRORVALUE = "newPwd";
	private static final Object NEW_PWD_INVALID_ERRORVALUE = "illegalNewPwd";
	private static final String DATA_ERRORVALUE = "data";
	private static final String DELETE_ERRORVALUE = "delete";
	private static final String ADD_ERRORVALUE = "add";

	private static final long serialVersionUID = 1L;
	

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public UserServlet() {
		super();
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		choosedoGetAction(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		choosedoPostAction(request, response);
	}

	/**
	 * manages the action requested by http Get request
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void choosedoPostAction(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		
		HttpSession session = request.getSession();
		String username = (String) session.getAttribute(SESSION_USERNAME);
		String actParam = request.getParameter("act");
		if (username == null||actParam==null) {
			redirectToHome(request, response);
			return;
		}
		if (actParam.equals(ACT_MODIFY_PASSWORD)) {
			processModifyPassword(request, response);
		} else if (actParam.equals(ACT_MODIFY_PERSONAL_DATA)) {
			processModifyPersonalData(request, response);
		} else if (actParam.equals(ACT_ADD_ABILITY)) {
			processAddAbility(request, response);
		}else if (actParam.equals(ACT_DELETE_ABILITY)) {
			processDeleteAbility(request, response); 
		}else {
			redirectToHome(request, response);
		}
	}
	/**
	 * manages the action requested by http Post request
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void choosedoGetAction(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		
		HttpSession session = request.getSession();
		String username = (String) session.getAttribute(SESSION_USERNAME);
		String actParam = request.getParameter("act");
		if (username == null||actParam==null) {
			redirectToHome(request, response);
			return;
		}
		
		if (actParam.equalsIgnoreCase(SHOW_PERSONAL_PROFILE)) {
			showPersonalProfile(request, response);
		} else if (actParam.equalsIgnoreCase(SHOW_MODIFY_PROFILE)) {
			showModifyProfile(request, response);
		} else if (actParam.equals(SHOW_MANAGE_ABILITIES)) {
			showManageAbilities(request, response);
		} else {
			redirectToHome(request, response);
		}

	}
	/**
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void processAddAbility(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {

		UserManagerRemote rem;
		try {
			rem = retrieveUserManagerRemote();
			String abilityName = request.getParameter(ABILITY_PARAM);
			HttpSession session = request.getSession();
			String username = ((String) session.getAttribute(SESSION_USERNAME));
			rem.addAbility(username, abilityName);
			showManageAbilities(request, response);
		} catch (ModifyException e) {
			request.setAttribute(ERROR_ATTRIBUTE, ADD_ERRORVALUE);
			showManageAbilities(request, response);
		} catch (NamingException e1) {
			redirectToError(response);
		}
	}
	/**
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void processDeleteAbility(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		UserManagerRemote rem;
		try {
			rem = retrieveUserManagerRemote();
			String abilityName = request.getParameter(ABILITY_PARAM);
			HttpSession session = request.getSession();
			String username = ((String) session.getAttribute(SESSION_USERNAME));
			rem.removeAbility(username, abilityName);
			showManageAbilities(request, response);
		} catch (ModifyException e) {
			request.setAttribute(ERROR_ATTRIBUTE, DELETE_ERRORVALUE);
			showManageAbilities(request, response);
		} catch (NamingException e1) {
			redirectToError(response);
		}
	}
	/**
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void processModifyPersonalData(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		UserManagerRemote rem;
		try {
			rem = retrieveUserManagerRemote();
			HttpSession session = request.getSession();
			String username = ((String) session.getAttribute(SESSION_USERNAME));
			String email = request.getParameter(EMAIL_REQPARAM);
			String city = request.getParameter(CITY_REQPARAM);
			String phone = request.getParameter(PHONE_REQPARAM);
			rem.modifyData(username, email, city, phone);
			request.setAttribute(ERROR_ATTRIBUTE, SUCCESS_DATA);
			showModifyProfile(request, response);
		} catch (ModifyException e) {
			redirectToError(response);
		} catch (NamingException e) {
			redirectToError(response);
		} catch (UserDataException e) {
			request.setAttribute(ERROR_ATTRIBUTE, DATA_ERRORVALUE);
			showModifyProfile(request, response);
		}

	}
	/**
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void processModifyPassword(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		UserManagerRemote rem;
		try {
			rem = retrieveUserManagerRemote();
			HttpSession session = request.getSession();
			String username = ((String) session.getAttribute(SESSION_USERNAME));
			String currentPwd = request.getParameter(CURRENT_PWD_REQPARAM);
			String newPwd1 = request.getParameter(NEW_PWD1_REQPARAM);
			String newPwd2 = request.getParameter(NEW_PWD2_REQPARAM);
			if (newPwd1.equals(newPwd2)) {
				rem.modifyPassword(username, currentPwd, newPwd1);
				request.setAttribute(ERROR_ATTRIBUTE, SUCCESS_PWD);
				showModifyProfile(request, response);
			} else {
				request.setAttribute(ERROR_ATTRIBUTE, NEW_PWD_ERRORVALUE);
				showModifyProfile(request, response);
			}
		} catch (ModifyException e) {
			request.setAttribute(ERROR_ATTRIBUTE, MODIFY_PASSWORD_ERRORVALUE);
			showModifyProfile(request, response);
		} catch (NamingException e) {
			redirectToError(response);
		} catch (UserDataException e) {
			request.setAttribute(ERROR_ATTRIBUTE, NEW_PWD_INVALID_ERRORVALUE);
			showModifyProfile(request, response);
		}
	}
	/**
	 * shows the list of declared abilities and the form to add a new ability
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void showManageAbilities(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		try {
			setDeclarableAbilities(request);
			setUserDetail(request);
			RequestDispatcher dispatcher = request
					.getRequestDispatcher(MANAGE_ABILITIES);
			dispatcher.forward(request, response);
		} catch (NamingException e) {
			redirectToError(response);
		} catch (UserNotFoundException e) {
			redirectToError(response);
		}
	}
	/**
	 * shows the form to modify profile or password
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void showModifyProfile(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		try {
			setUserDetail(request);
			RequestDispatcher dispatcher = request
					.getRequestDispatcher(MODIFY_PROFILE);
			dispatcher.forward(request, response);
		} catch (NamingException e) {
			redirectToError(response);
		} catch (UserNotFoundException e) {
			redirectToError(response);
		}

	}
	/**
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 * @throws ServletException
	 */
	private void showPersonalProfile(HttpServletRequest request,
			HttpServletResponse response) throws IOException, ServletException {
		try {
			UserProfileManagerRemote upmr;
			upmr = retrieveUserProfileManagerRemote();
			HttpSession session = request.getSession();
			String username = (String) session.getAttribute(SESSION_USERNAME);
			User u = upmr.getUserDetail(username);
			if (u != null) {
				setBlankValue(u);
				Collection<Feedback> feedbacks = upmr.getFeedbackOf(username);
				request.setAttribute(USER_REQPARAM, u);
				request.setAttribute(FEEDBACKS_REQPARAM, feedbacks);
				RequestDispatcher dispatcher = request
						.getRequestDispatcher(PERSONAL_PROFILE);
				dispatcher.forward(request, response);
			} else {
				redirectToError(response);
			}
		} catch (NamingException e) {
			redirectToError(response);
		} catch (UserNotFoundException e) {
			redirectToError(response);
		}

	}
	/**
	 * 
	 * @param user
	 */
	private void setBlankValue(User user) {
		if(user.getCity().equals("")){
			user.setCity("-");
		}
		if(user.getPhone().equals("")){
			user.setPhone("-");
		}		
	}
	/**
	 * retrieves the user details
	 * @param request
	 * @throws NamingException
	 * @throws UserNotFoundException
	 */
	private void setUserDetail(HttpServletRequest request)
			throws NamingException, UserNotFoundException {
		UserProfileManagerRemote upmr;
		upmr = retrieveUserProfileManagerRemote();
		HttpSession session = request.getSession();
		User u = upmr.getUserDetail((String) session
				.getAttribute(SESSION_USERNAME));
		request.setAttribute(SESSION_USERNAME, u);
		request.setAttribute(DECLARED_ABILITIES_REQPARAM, u.getDeclaredAbilities());
	}
	/**
	 * retrieves the set of all declarable abilities
	 * @param request
	 * @throws NamingException
	 */
	private void setDeclarableAbilities(HttpServletRequest request)
			throws NamingException {
		AbilityManagerRemote rem;
		rem = retrieveAbilityManagerRemote();
		Collection<String> declarableAbilities = rem.getDeclarableAbilities();
		request.setAttribute(DECLARABLE_ABILITIES_REQPARAM, declarableAbilities);
	}
	/**
	 * 
	 * @param response
	 * @throws IOException
	 */
	private void redirectToError(HttpServletResponse response)
			throws IOException {
		response.sendRedirect(ERROR_JSP);
	}
	/**
	 * 
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	private void redirectToHome(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		response.sendRedirect(INDEX_JSP);

	}
	/**
	 * retrieves the remote object
	 * @return
	 * @throws NamingException
	 */
	private UserManagerRemote retrieveUserManagerRemote()
			throws NamingException {
		Context jndiContext = new InitialContext();
		Object ref = jndiContext.lookup(RemoteBindingName.USER_MANAGER);
		UserManagerRemote rem = (UserManagerRemote) ref;
		return rem;
	}
	/**
	 * retrieves the remote object
	 * @return
	 * @throws NamingException
	 */
	private AbilityManagerRemote retrieveAbilityManagerRemote()
			throws NamingException {
		Context jndiContext = new InitialContext();
		Object ref = jndiContext.lookup(RemoteBindingName.ABILITY_MANAGER);
		AbilityManagerRemote rem = (AbilityManagerRemote) ref;
		return rem;
	}
	/**
	 * retrieves the remote object
	 * @return
	 * @throws NamingException
	 */
	private UserProfileManagerRemote retrieveUserProfileManagerRemote()
			throws NamingException {
		Context jndiContext = new InitialContext();
		Object ref = jndiContext.lookup(RemoteBindingName.USER_PROFILE_MANAGER);
		UserProfileManagerRemote rem = (UserProfileManagerRemote) ref;
		return rem;
	}
}
